/*
 Copyright (c) 2013, 2021, Oracle and/or its affiliates. All rights
 reserved.
 
 This program is free software; you can redistribute it and/or modify
 it under the terms of the GNU General Public License, version 2.0,
 as published by the Free Software Foundation.

 This program is also distributed with certain software (including
 but not limited to OpenSSL) that is licensed under separate terms,
 as designated in a particular file or component or in included license
 documentation.  The authors of MySQL hereby grant you an additional
 permission to link the program and your derivative works with the
 separately licensed software that they have included with MySQL.

 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License, version 2.0, for more details.

 You should have received a copy of the GNU General Public License
 along with this program; if not, write to the Free Software
 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 02110-1301  USA
 */
"use strict";
/*jslint newcap: true */
/*global t_basic, verify_t_basic, fail_verify_t_basic */

var path = require("path");
var ndbimpl;
try {
  ndbimpl = require(path.join(mynode.fs.build_dir, "ndb_adapter.node")).ndb.impl;
}
catch(e) {}

function assertVO(testCase, object, expected) {
  if(global.adapter == "ndb") {
    testCase.errorIfNotEqual("Expected NDB Value Object", expected,
                            ndbimpl.isValueObject(object));
  }
}

/*  Store a row and read it back
 *  Callback gets: (error, foundObject, session) 
*/ 
function persistAndFind(testCase, object, onFindCallback) {

  function onPersistThenFind(err, session) {
    session.find(t_basic, object.id, onFindCallback, session);
  }

  function onOpenThenPersist(session, testCase) { 
    testCase.session = session;
    assertVO(testCase, object, false);
    session.persist(t_basic, object, onPersistThenFind, testCase.session);
  }
    
  fail_openSession(testCase, onOpenThenPersist);
}

/* Generates a callback function to find a row,
   compare it to a known row, and pass or fail the test case.
*/
function makeFindAndCompare(testCase, session, refObject) {
  function onFindThenCompare(err, readObject) {
    var i, keys, k;
    testCase.errorIfError(err);
    testCase.errorIfNull("expected readObject on Find", readObject);
    assertVO(testCase, readObject, true);
    try {
      keys = Object.keys(refObject);
      for(i = 0 ; i < keys.length ; i++) {
        k = keys[i];
        testCase.errorIfNotEqual(k, refObject[k], readObject[k]);
      }
    }
    catch(e) {
      testCase.appendErrorMessage(e);
    }
    testCase.failOnError();  
  }

  return function findAndCompare(err) {
    testCase.errorIfError(err);
    session.find(t_basic, refObject.id, onFindThenCompare);
  };
}


// Find_Modify_Save 
var t1 = new harness.ConcurrentTest("Find_Modify_Save");
t1.run = function() {
  var row = new t_basic(23001, 'Snoopy', 23, 23001);
  
  /* Create a row with age = 23.
     Read it. 
     Update age to 29.
     Save it.
     Read it again.
  */

  function onFindThenSave(err, object, theSession) {
    assertVO(t1, object, true);
    object.age = 29; 
    t1.errorIfNotEqual("Age:Write_Read", 29, object.age);
    theSession.save(object, makeFindAndCompare(t1, theSession, object));
  }

  persistAndFind(this, row, onFindThenSave);
};


// Find_Modify_Update 
var t2 = new harness.ConcurrentTest("Find_Modify_Update");
t2.run = function() {
  var row = new t_basic(23002, 'Linus', 23, 23002); 
  
  function onFindThenUpdate(err, object, session) {
    assertVO(t2, object, true);
    object.age = object.age + 10;
    t2.errorIfNotEqual("Age:Read_Write_Read", row.age+10, object.age);
    session.update(object, makeFindAndCompare(t2, session, object));
  }
  
  persistAndFind(this, row, onFindThenUpdate);
};

// Find_Remove
var t3 = new harness.ConcurrentTest("Find_Remove");
t3.run = function() {
  var row = new t_basic(23003, 'Charlie Brown', 5, 23003);
  
  function onFindDeletedRow(err, obj) {
    t3.errorIfNotNull("found row should be null", obj);
    t3.failOnError();
  }
    
  function onDeleteThenFindAgain(err, session) {
    t3.errorIfError(err);
    session.find(t_basic, 20003, onFindDeletedRow);        
  }

  function onFindThenDelete(err, object, session) {
    t3.errorIfError(err);
    assertVO(t3, object, true);
    session.remove(object, onDeleteThenFindAgain, session);
  }
  
  persistAndFind(this, row, onFindThenDelete);
};


// Find_ModifyPK_Save
var t4 = new harness.ConcurrentTest("Find_ModifyPK_Save");
t4.run = function() {
  var row4a = new t_basic(23004, 'Lucy', 80, 23004);
  var row4b = new t_basic(23104, 'Lucy', 80, 23104);

  function onFindThenChange(err, object, session) {
    t4.errorIfError(err);
    assertVO(t4, object, true);
    object.id = 23104;
    object.magic = 23104;  // unique index on magic
    session.save(object, makeFindAndCompare(t4, session, row4b));
  }
  
  persistAndFind(this, row4a, onFindThenChange);
};

// Find_ModifyPK_Persist
var t5 = new harness.ConcurrentTest("Find_ModifyPK_Persist");
t5.run = function() {
  var row5a = new t_basic(23005, 'Sally', 4, 23005);
  var row5b = new t_basic(23105, 'Sally', 4, 23105);

  function onFindThenChange(err, object, session) {
    t5.errorIfError(err);
    assertVO(t5, object, true);
    object.id = 23105;
    object.magic = 23105;  // unique index on magic
    session.persist(object, makeFindAndCompare(t5, session, row5b));
  }
  
  persistAndFind(this, row5a, onFindThenChange);
};


// Find_ModifyPK_Delete
var t6 = new harness.ConcurrentTest("Find_ModifyPK_Delete");
t6.run = function() {
  var row6 = new t_basic(23006, 'Schroeder', 12, 23006);

  function onDeleteExpectError(err) { 
    t6.errorIfNull("Expected Error", err);
    t6.errorIfNotEqual("SQLSTATE", "02000", err.sqlstate);
    t6.failOnError();
  }

  function onFindThenModify(err, object, theSession) {
    assertVO(t6, object, true);
    object.id = 23106;
    theSession.remove(object, onDeleteExpectError);
  }

  persistAndFind(this, row6, onFindThenModify);
};


function onLoadThenCompare(err, testCase, expectedObject, loadedObject) {
  var i, keys, k;
  testCase.errorIfError(err);
  assertVO(testCase, loadedObject, true);
  try {
    keys = Object.keys(expectedObject);
    for(i = 0 ; i < keys.length ; i++) {
      k = keys[i];
      testCase.errorIfNotEqual(k, expectedObject[k], loadedObject[k]);
    }
  }
  catch(e) {
    testCase.appendErrorMessage(e);
  }
  testCase.failOnError();  
}

/* load uses the object it has and copies database values into it 
    based on finding the object in the database. */
// Find_ModifyPK_Load
var t7 = new harness.ConcurrentTest("Find_ModifyPK_Load");
t7.run = function() {
  var specialTag = 1;
  var row7a = new t_basic(23007, 'Pigpen', 23, 23007);
  var row7b = new t_basic(7, 'Employee 7', 7, 7);
  row7b.specialTag = specialTag;
  
  function onFindThenLoad(err, object, theSession) {
    assertVO(t7, object, true);
    object.id = 7; 
    object.specialTag = specialTag;
    theSession.load(object, onLoadThenCompare, t7, row7b, object);
  }

  persistAndFind(this, row7a, onFindThenLoad);
};


// Find_ModifyUK_Load
var t8 = new harness.ConcurrentTest("Find_ModifyUK_Load");
t8.run = function() {
  var specialTag = 1;
  var row8a = new t_basic(23008, 'Franklin', 23, 23008);
  var row8b = new t_basic(8, 'Employee 8', 8, 8);
  row8b.specialTag = specialTag;
 
  function onFindThenLoad(err, object, theSession) {
    assertVO(t8, object, true);
    object.magic = 8; 
    object.specialTag = specialTag;
/// NB: 
    object.id = undefined;
    theSession.load(object, onLoadThenCompare, t8, row8b, object);
  }

  persistAndFind(this, row8a, onFindThenLoad);
};



// implement getColumnMaskForVO(obj) and test masked columns 
exports.tests = [t1, t2, t3, t4, t5, t6, t7, t8];

